﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using WebPALTT.Lib;
using WebPALTT.Lib.Debug;
using System.Reflection;

namespace WebPALTT.UI
{
    public partial class DebugForm : Form
    {
        AppDomain mAppDomain;
        DebugRunner mRunner;
        List<Guid> mBreakpoint = new List<Guid> { };

        public DebugForm(Scenario scenario)
        {
            if (scenario == null)
                throw new NullReferenceException("Scenario cannot be null.");
            if (scenario.Actions.Count == 0)
                throw new Exception("Scenario must contain atleast one action.");

            InitializeComponent();
            tec_Request_CodeEditor.SetHighlighting("C#");
            tec_Request_Content.SetHighlighting("HTML");
            tec_Response_Content.SetHighlighting("HTML");

            mAppDomain = AppDomain.CreateDomain("DebugDomain");
            mRunner = (DebugRunner)mAppDomain.CreateInstanceFromAndUnwrap(
                Assembly.GetAssembly(typeof(DebugRunner)).Location,
                typeof(DebugRunner).FullName);

            mRunner.StartTest(scenario);
            RefreshTreeView();
        }

        private void DebugForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (mAppDomain != null)
                AppDomain.Unload(mAppDomain);
        }

        private void tsb_NextStep_Click(object sender, EventArgs e)
        {
            if (mRunner.CurrentStep == null)
                return;
            tssl_Status.Text = "Running Step...";
            mRunner.NextStep();
            RefreshTreeView();
            RefreshListView();

            if (mRunner.CurrentStep == null)
            {
                tssl_Status.Text = "Test Completed";
            }
            else
                tssl_Status.Text = "Ready";
        }

        private void tsb_Continue_Click(object sender, EventArgs e)
        {
            if (mRunner.CurrentStep == null)
                return;

            tssl_Status.Text = "Running Step...";
            bool stop = false;
            while (!stop)
            {
                mRunner.NextStep();
                if (mRunner.CurrentStep == null || mBreakpoint.Contains(mRunner.CurrentStep.Key))
                    stop = true;
            }
            RefreshTreeView();
            RefreshListView();

            if (mRunner.CurrentStep == null)
            {
                tssl_Status.Text = "Test Completed";
                tsb_NextStep.Enabled = false;
                tsb_Continue.Enabled = false;
            }
            else
                tssl_Status.Text = "Ready";
        }

        #region TreeView

        private void ToggleBreakPoint(object sender, EventArgs e)
        {
            TreeNode hitNode = tv_Actions.SelectedNode;

            if (tv_Actions.SelectedNode == null)
                return;
            Guid key;
            if (!Guid.TryParse(tv_Actions.SelectedNode.Name, out key))
                Guid.TryParse(tv_Actions.SelectedNode.Parent.Name, out key);

            if (!mBreakpoint.Contains(key))
                mBreakpoint.Add(key);
            else
                mBreakpoint.Remove(key);
            RefreshTreeView();
            TreeNode[] results = tv_Actions.Nodes.Find(key.ToString(), true);
            if (results.Length > 0)
                tv_Actions.SelectedNode = results[0];
        }

        private void RefreshTreeView()
        {
            if (mRunner == null)
                return;

            Scenario scenario = mRunner.Scenario;
            if (scenario == null)
                return;

            tv_Actions.Nodes.Clear();
            tv_Actions.Nodes.AddRange(GetTreeNodes(scenario.Actions.ToArray()));
            tv_Actions.ExpandAll();

            if (mRunner.CurrentStep != null)
            {
                TreeNode[] results = tv_Actions.Nodes.Find(mRunner.CurrentStep.Key.ToString(), true);
                if (results.Length == 1)
                    results[0].StateImageKey = "Yellow";
            }
        }

        private TreeNode[] GetTreeNodes(ActionItem[] actions)
        {
            List<TreeNode> list = new List<TreeNode> { };
            foreach (ActionItem item in actions)
            {
                TreeNode newnode = null;
                if (item.GetType().Equals(typeof(ActionItem_WebRequest)))
                    newnode = new TreeNode(item.Name, 1, 1);
                else if (item.GetType().Equals(typeof(ActionItem_Script)))
                    newnode = new TreeNode(item.Name, 1, 1);
                else if (item.GetType().Equals(typeof(ActionItem_Sleep)))
                    newnode = new TreeNode(item.Name, 1, 1);
                else if (item.GetType().Equals(typeof(ActionItem_If)))
                {
                    newnode = new TreeNode(item.Name, 1, 1);
                    newnode.Nodes.Add(new TreeNode("Then", 0, 0));
                    newnode.Nodes.Add(new TreeNode("Else", 0, 0));
                    newnode.Nodes[0].Nodes.AddRange(GetTreeNodes(((ActionItem_If)item).ThenActions.ToArray()));
                    newnode.Nodes[1].Nodes.AddRange(GetTreeNodes(((ActionItem_If)item).ElseActions.ToArray()));
                }
                else if (item.GetType().Equals(typeof(ActionItem_Loop)))
                {
                    newnode = new TreeNode(item.Name, 1, 1);
                    newnode.Nodes.AddRange(GetTreeNodes(((ActionItem_Loop)item).Actions.ToArray()));
                }
                else if (item.GetType().Equals(typeof(ActionItem_Variable)))
                    newnode = new TreeNode(item.Name, 1, 1);
                
                newnode.Name = item.Key.ToString();
                newnode.StateImageKey = "None";
                if (mBreakpoint.Contains(item.Key))
                    newnode.ImageKey = "Red";
                else
                    newnode.ImageKey = "White";
                if (newnode != null)
                    list.Add(newnode);
            }
            return list.ToArray();        
        }

        private void tv_Actions_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                Point pt = new Point(e.X, e.Y);
                TreeNode node = tv_Actions.GetNodeAt(pt);
                if (node != null)
                {
                    tv_Actions.SelectedNode = node;
                    cms_Breakpoints.Show(tv_Actions, pt);
                }
            }
        }

        private void tv_Actions_BeforeSelect(object sender, TreeViewCancelEventArgs e)
        {
            e.Node.SelectedImageKey = e.Node.ImageKey;
        }
        #endregion

        #region ListView

        private void RefreshListView()
        {
            if (mRunner == null)
                return;

            DebugResultItem[] results = mRunner.Results;
            if (results == null)
                return;

            lv_Results.Items.Clear();
            List<ListViewItem> items = new List<ListViewItem> { };
            for (int i = 0; i < results.Length;i++ )
            {
                ListViewItem lv_item = new ListViewItem(results[i].Name);
                lv_item.SubItems.Add((i+1).ToString());
                lv_item.SubItems.Add(results[i].ActionType.ToString());
                double duration = ((TimeSpan)results[i].Results["DURATION"]).TotalSeconds;
                lv_item.SubItems.Add(duration.ToString("F4"));
                if (results[i].Errors.Count > 0)
                    lv_item.StateImageIndex = 1;
                else
                    lv_item.StateImageIndex = 0;
                lv_item.Name = results[i].Key.ToString();
                items.Add(lv_item);
            }
            lv_Results.Items.AddRange(items.ToArray());
        }

        private void lv_Results_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (lv_Results.SelectedItems.Count != 1)
                return;

            ListViewItem selection = lv_Results.SelectedItems[0];
            Guid key = Guid.Parse(selection.Name);
            DebugResultItem[] results = mRunner.Results;
            DebugResultItem debugResult = null;
            for (int i = 0; i < results.Length; i++)
                if (Guid.Equals(results[i].Key, key) && results[i].Name.Equals(selection.Text))
                    debugResult = results[i];
            if (debugResult == null)
                throw new Exception("Result item cound not be found.");

            ActionItem actionItem = mRunner.Scenario.Actions.GetChild(key);
            if(actionItem == null)
                throw new Exception("Action item cound not be found.");

            dgv_Request_Headers.Rows.Clear();
            tec_Request_Content.Text = "";
            tec_Request_Content.Refresh();            
            tec_Request_CodeEditor.Text = "";
            tec_Request_CodeEditor.Refresh();
            dgv_Result_Result.Rows.Clear();
            dgv_Result_Info.Rows.Clear();
            dgv_Response_Headers.Rows.Clear();
            tec_Response_Content.Text = "";
            tec_Response_Content.Refresh();

            tc_RequestInfo.TabPages.Clear();
            tc_ResultInfo.TabPages.Clear();

            tc_RequestInfo.TabPages.Add(tp_Request_Info);
            tc_ResultInfo.TabPages.Add(tp_Result_Results); 
            tc_ResultInfo.TabPages.Add(tp_Result_Info);
            
            switch (actionItem.ActionType)
            {
                case ActionType.If:
                    tc_RequestInfo.TabPages.Add(tp_Request_Code);
                    break;
                case ActionType.Script:
                    goto case ActionType.If;
                case ActionType.Variable:
                    goto case ActionType.If;
                case ActionType.WebRequest:
                    tc_RequestInfo.TabPages.Add(tp_Request_Headers);
                    tc_RequestInfo.TabPages.Add(tp_Request_Content);
                    tc_ResultInfo.TabPages.Add(tp_Result_Headers); 
                    tc_ResultInfo.TabPages.Add(tp_Result_Content);
                    break;
            }
            // Request - Info
            pg_Request_Info.SelectedObject = actionItem;

            // Result - Results
            foreach (KeyValuePair<string, object> kvp in debugResult.Results)
                dgv_Result_Result.Rows.Add(kvp.Key, kvp.Value.ToString());

            // Result - Info
            foreach (KeyValuePair<string, object> kvp in debugResult.Data)
                dgv_Result_Info.Rows.Add(kvp.Key, kvp.Value.ToString());

            if (debugResult.ActionType == ActionType.WebRequest)
            {
                HttpResponse response = (HttpResponse)debugResult.Data["RESPONSE"];
                ActionItem_WebRequest request = (ActionItem_WebRequest)actionItem;
                // Result - Header
                dgv_Response_Headers.Rows.Add("ResponseUri", response.ResponseUri);
                dgv_Response_Headers.Rows.Add("Method", response.Method);
                dgv_Response_Headers.Rows.Add("StatusCode", response.StatusCode);
                dgv_Response_Headers.Rows.Add("StatusDescription", response.StatusDescription); 
                dgv_Response_Headers.Rows.Add("ContentEncoding", response.ContentEncoding);
                dgv_Response_Headers.Rows.Add("ContentLength", response.ContentLength);
                dgv_Response_Headers.Rows.Add("ContentType", response.ContentType);
                dgv_Response_Headers.Rows.Add("Cookies", response.Cookies.ToString());

                // Result - Content
                tec_Response_Content.Text = response.Content;
                tec_Response_Content.Refresh(); 

                // Request - Header
                foreach (KeyValuePair<string, string> kvp in request.Headers)
                    dgv_Request_Headers.Rows.Add(kvp.Key, kvp.Value);

                // Request - Content
                tec_Request_Content.Text = request.Content;
                tec_Request_Content.Refresh(); 
            }
            if (debugResult.ActionType == ActionType.Script)
            {
                tec_Request_CodeEditor.Text = ((ActionItem_Script)actionItem).Code;
                tec_Request_CodeEditor.Refresh(); 
            }
            else if (debugResult.ActionType == ActionType.If)
            {               
                tec_Request_CodeEditor.Text = "if(" + ((ActionItem_If)actionItem).Condition + ")\n{\n\tThen;\n}\nelse\n{\n\tElse;\n}";
                tec_Request_CodeEditor.Refresh(); 
            }
            else if (debugResult.ActionType == ActionType.Variable)
            {
                tec_Request_CodeEditor.Text = "private " + (((ActionItem_Variable)actionItem).IsStatic ? "static " : "") +
                    ((ActionItem_Variable)actionItem).Type + " " + actionItem.Name + (String.IsNullOrEmpty(((ActionItem_Variable)actionItem).Value) ? "" : " = " +
                    ((ActionItem_Variable)actionItem).Value) + ";";
                tec_Request_CodeEditor.Refresh(); 
            }            
        }

        #endregion

        private void DebugForm_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.F5)
                tsb_NextStep_Click(null, EventArgs.Empty);
            else if (e.KeyCode == Keys.F6)
                tsb_Continue_Click(null, EventArgs.Empty);
            else if (e.KeyCode == Keys.F7)
                tsb_Stop_Click(null, EventArgs.Empty);
            else if (e.KeyCode == Keys.F9)
                ToggleBreakPoint(null, EventArgs.Empty);
            else
                return;
            e.Handled = true;
        }

        private void tsb_Stop_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}
